
var refineAttributes = ['attack', 'defence', 'mdefence'];
var refineLevel = {
    'NORMAL' : 0,
    'SILVER' : 1,
    'SUPER' : 2,
};

var horseQuantityMap = {
    'green' : 1,
    'blue' : 1,
    'purple' : 1,
    'orange' : 1,
};

//羽翼相关定义值
var wingDefines = {
    'stage' : {
        'common' : 0,  //仙羽
        'demon' : 1,  //魔羽
    },

    'stageMaxLevel' : 10,  //羽翼每阶段最高等级
};

var GodDefines = {
    'god' : 2,      // 觉醒
    'lord' : 3,     // 化神
}

exports.train = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) || isNaN(req.args.pos) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = +req.args.rid;
        var pos = +req.args.pos;

        var user = player.user;
        
        if( !(pos in user.role_train) ) {
            resp.code = 1; resp.desc = 'seat is not open'; break;
        }

        if( !(rid in user.role) ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var role = user.role[rid];
        if( role.level >= user.status.level ) {
            resp.code = 1; resp.desc = 'role reach max level'; break;
        }

        var now = common.getTime();
        var trainSeat = user.role_train[pos];

        if( trainSeat.timer > now  ) {
            resp.code = 1; resp.desc = 'seat is busying'; break;
        }

        var levelConf = gConfLevel[user.status.level];
        if( !levelConf ) {
            resp.code = 1; resp.desc = 'level error'; break;
        }

        var gainXp = levelConf.TrainingXp;
        if( trainSeat.level > 0 ) {
            var trainingConf = gConfTraining[pos];
            gainXp = Math.floor(gainXp * (trainingConf['XpFactor'+trainSeat.level] / 100 + 1));
        }

        if( user.status.vip ) {
            gainXp *= 1 + gConfVip[user.status.vip].RoleTrainAdd / 100;
        }
        
        player.addRoleXp(role, gainXp);
        player.roleDirty = true;

        trainSeat.timer = now + (+gConfGlobal.TrainingColdTime);
        player.markDirty('role_train.' + pos);

        player.doTask('TrainRole','all');

        resp.data.train_seat = trainSeat;
        resp.data.xp = role.xp;
        resp.data.level = role.level;
        
    }while(false);

    onHandled();
}

exports.clear_seat = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.pos) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var pos = +req.args.pos;

        var user = player.user;
        
        if( !(pos in user.role_train) ) {
            resp.code = 1; resp.desc = 'seat is not open'; break;
        }
        
        var now = common.getTime();
        var trainSeat = user.role_train[pos];
        if( trainSeat.timer <= now ) {
            resp.code = 1; resp.desc = 'train seat is not colding'; break;
        }
        
        var coldHour = (trainSeat.timer - now)/3600;
        var cashCost = Math.ceil(coldHour * (+gConfGlobal.TrainingClearColdTime));

        if (!player.costResource('cash', cashCost, pos)) {
            resp.code = 1; resp.desc = 'not enough cash'; break;
        }

        trainSeat.timer = 0;
        player.markDirty('role_train.'+pos);

        resp.data.cash = -cashCost;
    }while(false);

    onHandled();
}

exports.clear_all_seat = function(player, req, resp, onHandled) {
    do {

        var user = player.user;
        
        var cashCost = 0;
        var now = common.getTime();
        for( var pos in user.role_train ) {
            var trainSeat = user.role_train[pos];
            if( trainSeat.timer <= now ) {
                continue; 
            }
            
            var coldHour = (trainSeat.timer - now)/3600;
            cashCost += Math.ceil(coldHour * (+gConfGlobal.TrainingClearColdTime));
        }

        if (!player.costResource('cash', cashCost)) {
            resp.code = 1; resp.desc = 'not enough cash'; break;
        }

        for( var pos in user.role_train ) {
            var trainSeat = user.role_train[pos];
            trainSeat.timer = 0;
        }

        player.markDirty('role_train');

        resp.data.cash = -cashCost;
    }while(false);

    onHandled();
}

exports.upgrade_seat = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.pos) || !gConfTraining[req.args.pos] ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var pos = +req.args.pos;
        var user = player.user;

        var trainingConf = gConfTraining[pos];
        if (!trainingConf) {
            resp.code = 1; resp.desc = 'pos invalid'; break;
        }

        var trainSeat = user.role_train[pos];
        var cashCost = 0;

        if (!trainSeat) {
            // 开启训练位
            var canOpen = false;
            if( user.status.level >= trainingConf.OpenLevel ) {
                canOpen = true;
            }

            if( user.status.vip && pos <= (gConfVip[user.status.vip].OpenTrainSeat + 2) ) {
                canOpen = true;
            }

            if( !canOpen ) {
                resp.code = 1; resp.desc = 'not reach open limit'; break;
            }

            trainSeat = {'timer':0, 'level':0};
        } else {
            if (trainSeat.level >= 2) {
                resp.code = 1; resp.desc = 'level up to max'; break;
            }

            if (Object.keys(user.role_train).length < 9) {
                resp.code = 1; resp.desc = 'not all opened'; break;
            }

            trainSeat.timer = 0;
            trainSeat.level += 1;
            cashCost = trainingConf['UpCash'+trainSeat.level];
        }

        if (!player.costResource('cash', cashCost, pos)) {
            resp.code = 1; resp.desc = 'not enough cash'; break;
        }

        user.role_train[pos] = trainSeat;
        player.markDirty('role_train.'+pos);

        resp.data.train_seat = trainSeat;
        resp.data.cash = -cashCost;
    }while(false);

    onHandled();
}

exports.transfer = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) || isNaN(req.args.trid) ) {
            resp.code = 1; resp.desc = 'no valid args: rid or trid'; break;
        }

        var rid = +req.args.rid;
        var trid = +req.args.trid;
        var useCash = req.args.cash ? 1:0;

        var user = player.user;

        if( !(rid in user.role) || !(trid in user.role) ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var role = user.role[rid];
        var trole = user.role[trid];

        if( trole.level >= user.status.level ) {
            resp.code = 1; resp.desc = 'not permitted'; break;
        }

        if( role.xp <= 0 ) {
            resp.code = 1; resp.desc = 'role no xp'; break;
        }

        var goldCost = 0;
        var cashCost = 0;
        if( useCash ) {
            cashCost = +gConfGlobal.RoleTransferCash;
            if (!player.costResource('cash', cashCost, trid, rid)) {
                resp.code = 1; resp.desc = 'not enough cash'; break;
            }
        }else{
            goldCost = +gConfGlobal.RoleTransferGold;
            if (!player.costResource('gold', goldCost, trid, rid)) {
                resp.code = 1; resp.desc = 'not enough gold'; break;
            }
        }

        var addXp = 0;
        if( useCash ) {
            addXp = Math.floor(role.xp * (+gConfGlobal.RoleTransferCashXp) /100);
        }else{
            addXp = Math.floor(role.xp * (+gConfGlobal.RoleTransferGoldXp) /100);
        }

        role = user.role[rid]; 
        player.addRoleXp(role, -role.xp);

        /*
        var oldLevel = player.getRoleLevel(trole);
        trole.xp += addXp;
        trole.level = player.getRoleLevel(trole);
        if( trole.level > user.status.level ) {
            trole.level = user.status.level; 
            trole.xp = gConfRoleLevel[trole.level].Xp - 1;
        }

        if( oldLevel != trole.level ) {
            player.markDirty(util.format('role.%d.level', trole.id));
        }

        player.markDirty(util.format('role.%d.xp', trole.id));
        player.roleDirty = true;
        */

        player.addRoleXp(trole, addXp);

        resp.data.gold = -goldCost;
        resp.data.cash = -cashCost;
        resp.data.xp = role.xp;
        resp.data.level = role.level;
        resp.data.txp = trole.xp;
        resp.data.tlevel = trole.level;
    }while(false);

    onHandled();
}

exports.refine = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) || isNaN(req.args.level) ) {
            resp.code = 1; resp.desc = 'no valid args: rid'; break;
        }
        
        var isTen = req.args.ten ? 1 : 0;
        var rid = +req.args.rid;
        var user = player.user;
        if( !(rid in user.role) ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var level = Math.floor(+req.args.level);
        if( !(level == refineLevel.NORMAL || level == refineLevel.SILVER || level == refineLevel.SUPER) ) {
            resp.code = 1; resp.desc = 'invalid level'; break;
        }

        var vip = user.status.vip;
        if( (refineLevel.SILVER == level && vip < 2) || (refineLevel.SUPER == level && vip < 5)) {
            resp.code = 1; resp.desc = 'no root'; break;
        }

        var costCash = 0;
        if( refineLevel.SILVER == level ) {
            costCash = gConfGlobal.SilverRoleRefineCash;
        }else if( refineLevel.SUPER == level ) {
            costCash = gConfGlobal.SuperRoleRefineCash;
        }
        
        costCash = isTen ? (10 * costCash) : costCash;

        if( costCash > 0 && user.status.cash < costCash ) {
            resp.code = 1; resp.desc = 'not enought cash'; break;
        }
        
        var materialCostNum = isTen ? 10 : 1; 
        var materialCost = {};
        if( !player.addInventory('material', gConfGlobal.RoleRefineMaterial, -materialCostNum) ) {
            resp.code = 1; resp.desc = 'no enough material'; break;
        }
        
        materialCost[gConfGlobal.RoleRefineMaterial] = -materialCostNum;

        var role = user.role[rid];

        var refine = {
            attack: 0,
            defence: 0,
            mdefence: 0
        };

        var refineMax = 40 + 20*role.level;
        if( refineLevel.NORMAL == level ) {
            var min=gConfGlobal.RoleRefineMin,max=gConfGlobal.RoleRefineMax;
        }else if( refineLevel.SILVER == level ) {
            var min=gConfGlobal.SilverRoleRefineMin,max=gConfGlobal.SilverRoleRefineMax;
        }else if ( refineLevel.SUPER == level ) {
            var min=gConfGlobal.SuperRoleRefineMin,max=gConfGlobal.SuperRoleRefineMax;
        }

        var min = isTen ? 10*min : min;
        var max = isTen ? 10*max : max;

        for( var attr in refine ) {
            var refined = common.randRange(min, max);
            var origin = role[attr];
            if( (origin + refined) > refineMax ) {
                refined = refineMax - origin;
            }

            refine[attr] = refined;

            role[attr] += refined;
            player.markDirty(util.format('role.%d.%s', rid, attr));
        }

        player.roleDirty = true;

        if( costCash > 0 ) {
            player.costResource('cash', costCash, level, rid);
            resp.data.cash = -costCash;
        }
        
        refine.available = 1;
        refine.rid = rid;

        user.role_refine = refine;
        player.markDirty('role_refine');

        resp.data.material = materialCost;
        resp.data.attack = refine.attack;
        resp.data.defence = refine.defence;
        resp.data.mdefence = refine.mdefence;
        resp.data.cash = -costCash;

        player.doEventRoleRefine(role);
    }while(false);

    onHandled();
}

exports.upgrade_grouth = function(player, req, resp, onHandled) {
    do {
        if (isNaN(req.args.rid)) {
            resp.code = 1; resp.desc = 'no valid args: rid'; break;
        }

        var rid = +req.args.rid;
        var type = +req.args.type;
        var user = player.user;
        
        var role = user.role[rid];
        if (!role) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var roleConf = gConfRole[rid];
        var breakConf = gConfGrouthBreak[role.grouth_break];
        var soldierConf = gConfSoldier[+roleConf.Soldier][role.soldier_level];
        
        var maxGrouth = gConfGrouth[roleConf.Quantity]['Max'+role.soldier_level];
        if (role.grouth_break) {
            maxGrouth = breakConf.Grouth;
        }
        
        if (role.grouth >= maxGrouth) {
            resp.code = 1; resp.desc = 'up to max'; break;
        }

        var succRate = 0;
        var cashCost = 0;
        var materialCost = {};
      
        var grouthGive = player.getRoleGrouthGive();
        var grouthLevel = role.soldier_level;
        var grouthMaterialNum = 1;
        var grouthMaterialId = soldierConf.GrouthMaterial;
        if (role.grouth_break) {
            grouthMaterialId = breakConf.GrouthMaterial;
            grouthLevel += role.grouth_break;
        }

        if (player.getInventory('material', grouthMaterialId) < grouthMaterialNum) {
            resp.code = 1; resp.desc = 'not enough material'; break;
        }
        
        if (1 == type) { //至尊培养
            if (role.grouth_break) {
                cashCost = breakConf.GrouthCash;
            } else {
                cashCost = soldierConf.GrouthCash;                
            }

            if (!player.costResource('cash', cashCost, grouthLevel, rid)) {
                resp.code = 1; resp.desc = 'not enough cash'; break;
            }
            
            succRate = 101;
        } else if (2 == type) { //幸运培养
            var grouthStarNum = null;
            var grouthStarId = null;
            if (role.grouth_break) {
                grouthStarNum = breakConf.GrouthStarNum;
                grouthStarId = breakConf.GrouthStarId;
            } else {
                grouthStarNum = soldierConf.GrouthStarNum;
                grouthStarId = soldierConf.GrouthStarId;
            }

            if (!grouthStarNum || !grouthStarId) {
                resp.code = 1; resp.desc = 'can not star grouth'; break;
            }
            
            if (!player.addInventory('material', grouthStarId, -grouthStarNum)) {
                resp.code = 1; resp.desc = 'not enough star'; break;
            }

            materialCost[grouthStarId] = -grouthStarNum;
            succRate = 101;
        } else if (3 == type) {
            var needGrouthGive = role.grouth_break ? breakConf.GrouthGive : soldierConf.GrouthGive; 
            var levelGrouthGive = grouthGive[grouthLevel] || 0;
            if (levelGrouthGive < needGrouthGive) {
                resp.code = 1; resp.desc = 'grouth give not reach'; break;
            }
            grouthGive[grouthLevel] = 0;
            player.markDirty('role_grouth_give');
            succRate = 101;
        } else { //普通培养
            if (role.grouth_break) {
                succRate = breakConf.GrouthRate;
            } else {
                succRate = soldierConf.GrouthRate;
            }

            if (user.status.vip) {
                succRate += gConfVip[user.status.vip].UpgradeGrouthAdd;
            }
        }

        if (!player.addInventory('material', grouthMaterialId, -grouthMaterialNum)) {
            resp.code = 1; resp.desc = 'not enough star'; break;
        }
        materialCost[grouthMaterialId] = -grouthMaterialNum;
        
        var succFlag = 0;
        if (common.randRange(1, 100) <= succRate) {
            succFlag = 1;
            role.grouth += 1;
            player.markDirty(util.format('role.%d.grouth', rid));
        } else {
            addAttrValue(grouthGive, grouthLevel, common.randRange(8, 15));
            player.markDirty('role_grouth_give');
        }

        player.roleDirty = true;
        
        if (succFlag && (role.grouth == 200 || role.grouth == 250)) {
            gMessage.addSystemMessage('role_grouth', [user.info.un, roleConf.Name, role.grouth]);
        }

        resp.data.grouth = role.grouth;
        resp.data.material = materialCost;
        resp.data.cash = -cashCost;
        resp.data.role_grouth_give = grouthGive;
        
        player.doEventRoleUpgradeGrouth(role);
    }while(false);

    onHandled();
}

exports.upgrade_army = function(player, req, resp, onHandled) {
    do {
        if( !req.args.rid ) {
            resp.code = 1; resp.desc = 'no valid args: rid'; break;
        }

        var rid = +req.args.rid;

        var user = player.user;

        var roleConf = gConfRole[rid];
        if( !roleConf || !(rid in user.role) ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var grouthConf = gConfGrouth[roleConf.Quantity];

        var role = user.role[rid];
        if( role.grouth < +grouthConf['Max'+role.soldier_level] ) {
            resp.code = 1; resp.desc = 'grouth not reach max'; break;
        }

        var newLevel = role.soldier_level+1;
        if( !grouthConf['Min'+newLevel] ) {
            resp.code = 1; resp.desc = 'soldier reach max level'; break;
        }

        var soldierConf = gConfSoldier[+roleConf.Soldier][newLevel];
        if( +soldierConf.RoleLevel > player.getRoleLevel(role) ) {
            resp.code = 1; resp.desc = 'role level not reach'; break;
        }

        // 判断资源
        var materialCost = {};
        if( newLevel > 1 ) {
            var materialType = soldierConf.Material;
            var materialNum = -soldierConf.MaterialNum;
            if( !player.addInventory('material', materialType, materialNum) ) {
                resp.code = 1; resp.desc = 'not enough material'; break;
            }
            materialCost[materialType] = materialNum;
        }

        role.soldier_level = newLevel;
        role.grouth = grouthConf['Min'+newLevel];

        player.markDirty(util.format('role.%d.soldier_level', rid));
        player.markDirty(util.format('role.%d.grouth', rid));
        player.roleDirty = true;

        player.doTask('UpgradeSoldier','all');
        player.checkEvent('RoleGrouthSum');
        
        if( !grouthConf['Min'+(role.soldier_level+1)] ) {
            gMessage.addSystemMessage('role_army_max', [user.info.un, roleConf.Name]);
        }

        resp.data.soldier_level = role.soldier_level;
        resp.data.grouth = role.grouth;
        resp.data.material = materialCost;


    }while(false);

    onHandled();
}

exports.upgrade_skill = function(player, req, resp, onHandled) {
    do {
        if( !req.args.rid || !req.args.skill ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = +req.args.rid;
        var skillId = +req.args.skill;

        var user = player.user;

        if( !(rid in user.role) ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var role = user.role[rid];
        var roleConf = gConfRole[rid];

        if (!gIsStandSkill(skillId)) {
            if (!role.god) {
                resp.code = 1; resp.desc = 'god level not reach'; break;
            }

            if (!(skillId in role.skill_levels)) { 
                resp.code = 1; resp.desc = 'invalid gol skill id'; break;
            }
        }

        var skillKind = gGetSkillKind(roleConf.Soldier, skillId);
        var skillConf = gConfSkill[skillKind][skillId];
        if( !skillConf ) {
            resp.code = 1; resp.desc = 'invalid skill id'; break;
        }

        if( player.getRoleLevel(role) < skillConf.RoleLevel ) {
            resp.code = 1; resp.desc = 'role level not reach'; break;
        }
        
        var newSkillLevel = 1;
        if( !(skillId in role.skill_levels) ) {
            role.skill_levels[skillId] = newSkillLevel;
        }else if( role.skill_levels[skillId] >= +gConfGlobal.RoleSkillLevelMax ) {
            resp.code = 1; resp.desc = 'up to max'; break;
        }else {
            newSkillLevel = role.skill_levels[skillId] + 1;
        }

        // 判断资源
        var materialCost = {};
        if( newSkillLevel > 1 ) {
            var materialType = skillConf.Material;
            var materialNum = -skillConf['MaterialNum'+newSkillLevel]
            if( !player.addInventory('material', materialType, materialNum) ) {
                resp.code = 1; resp.desc = 'not enough material'; break;
            }

            materialCost[materialType] = materialNum;
            role.skill_levels[skillId] = newSkillLevel;
        }

        player.markDirty(util.format('role.%d.skill_levels.%d', rid, skillId));
        player.roleDirty = true;

        if (newSkillLevel >= gSkillFullLevel) {
            if (gIsStandSkill(skillId) ) {
                var skillFull = true;
                for (var i = 1; i <= 5; ++i) {
                    if ((role.skill_levels[i] || 0) < gSkillFullLevel) {
                        skillFull = false;
                        break;
                    }
                }

                if (skillFull) {
                    gMessage.addSystemMessage('skill_full', [user.info.un, roleConf.Name]);
                }
            }
            else {
                gMessage.addSystemMessage('skill_'+skillKind+'_full', [user.info.un, roleConf.Name]);
            }
        }

        resp.data.level = newSkillLevel;
        resp.data.material = materialCost;
        
        player.doEventRoleUpgradeSkill(role, skillId);
    }while(false);

    onHandled();
}

exports.wear = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) || isNaN(req.args.id) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = +req.args.rid;
        var id = +req.args.id;  // 装备或者技能ID
        var isSkill = req.args.isskill ? 1:0;
        
        var user = player.user;

        if( !(rid in user.role) ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var role = user.role[rid];
        var roleConf = gConfRole[rid];
        
        if( isSkill ) {
            if( [1,3,5].indexOf(id) < 0) {
                resp.code = 1; resp.desc = 'skill id error'; break;
            }

            if( !role.skill_levels[id] ) {
                resp.code = 1; resp.desc = 'not open'; break;
            }

            role.skill = id;
            player.markDirty(util.format('role.%d.skill', rid));
        }else{
            var equip = user.equip[id];
            if( !equip ) {
                resp.code = 1; resp.desc = 'no equip'; break;
            }

            var equipWeard = player.getEquipWeard();
            if( equipWeard[id] ) {
                resp.code = 1; resp.desc = 'not take off'; break;
            }

            var equipType = equip.type;
            if ('magic' == equipType) {
                equipType = 'weapon';
                if (roleConf.Soldier <= 3) {
                    resp.code = 1; resp.desc = 'invalid type'; break;
                }
            } else if ('weapon' == equipType) {
                if (roleConf.Soldier > 3) {
                    resp.code = 1; resp.desc = 'invalid type'; break;
                }
            }

            player.roleAddEquip(role, equip);
            player.doTask('WearEquip', 'all');
        }

        player.roleDirty = true;

    }while(false);
    
    onHandled();
}

exports.kill = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = +req.args.rid;
        var isCash = req.args.cash ? true : false;

        if (rid == gDefaultRoleId) {
            resp.code = 1; resp.desc = 'is default role'; break;
        }

        var user = player.user;
        var role = user.role[rid];
        if( !gConfRole[rid] || !role ) {
            resp.code = 1; resp.desc = 'not valid rid'; break;
        }
        
        var inTeam = false;
        for( var pos in user.team ) {
            if( rid == user.team[pos] ) {
                inTeam = true;
                break;
            }
        }

        if( inTeam ) {
            resp.code = 1; resp.desc = 'in team'; break;
        }

        var costCash = 0;
        var roleConf = gConfRole[rid];
        if (isCash) {
            if (roleConf.Quantity != 'orange') {
                resp.code = 1; resp.desc = 'no cash kill'; break;
            }

            costCash = gConfGlobal.KillRoleCostCash;
            if (!player.costResource('cash', costCash, rid)) {
                resp.code = 1; resp.desc = 'no enough cash'; break;
            }
        }
        
        var soldierKind = gConfRole[rid].Soldier;
        var addMaterials = {};
        var addSoul = 0;
        var addRoleCard = 1;
        var addHorseCurrency = 0;
        var addHonor = 0;
        var batchAwards = new common.BatchAwards();

        // 技能升级消耗的技能书和神之书
        var skillBooks = {};
        for (var skillId in role.skill_levels) {
            var skillLevel = role.skill_levels[skillId];
            var skillKind = gGetSkillKind(soldierKind, skillId);
            var skillConf = gConfSkill[skillKind][skillId];

            for (var i = 2; i <= skillLevel; ++i) {
                var materialNum = skillConf['MaterialNum'+i];
                addAttrValue(skillBooks, skillConf.Material, materialNum);
            }
        }

        if (!isCash) {
            for (var materialId in skillBooks) {
                skillBooks[materialId] = Math.floor(skillBooks[materialId] * 0.5);
            }
        } else {
            var soldierLevel = role.soldier_level; 
            var soldierConf = gConfSoldier[soldierKind];
            

            // 兵种: 兵符
            for (var level = 2; level <= soldierLevel; ++level) {
                var soldierLevelConf = soldierConf[level];
                if (!soldierLevelConf) break;

                addAttrValue(addMaterials, soldierLevelConf.Material, soldierLevelConf.MaterialNum);
            }
       
            // 洗髓: 洗髓丹
            var addRefineMeterial = Math.floor((role.attack + role.defence + role.mdefence)/4.5 * 0.8);
            addAttrValue(addMaterials, gConfGlobal.RoleRefineMaterial, addRefineMeterial);

            // 培养/培养突破: 培养丹, 神将碎片, 黑莲花
            var grouth = role.grouth || 0;
            var grouthConf = gConfGrouth.orange;
            for (var level = 1; level <= soldierLevel; ++level) {
                var levelMinGrouth = grouthConf['Min'+level] || roleConf.Grouth;
                var levelMaxGrouth = grouthConf['Max'+level];
                
                var soldierLevelConf = soldierConf[level];
                if (!soldierLevelConf || grouth <= levelMinGrouth) break;
                
                var addGrouthMaterial = Math.min(grouth, levelMaxGrouth) - levelMinGrouth;
                addAttrValue(addMaterials, soldierLevelConf.GrouthMaterial, addGrouthMaterial);
                
                if (soldierLevelConf.GrouthStarNum && soldierLevelConf.GrouthStarId) {
                    var addGrouthStar = (Math.min(grouth, levelMaxGrouth) - levelMinGrouth) * soldierLevelConf.GrouthStarNum;
                    addAttrValue(addMaterials, soldierLevelConf.GrouthStarId, addGrouthStar);
                }
            }

            var grouthBreakLevel = role.grouth_break || 0;
            for (var level = 0; level < grouthBreakLevel; ++level) {
                var breakLevelConf = gConfGrouthBreak[level];
                var breakNextLevelConf = gConfGrouthBreak[level + 1];
                if (!breakLevelConf || !breakNextLevelConf || grouth < breakLevelConf.Grouth) break;

                addAttrValue(addMaterials, breakLevelConf.Material, breakLevelConf.MaterialNum);
                
                var addGrouthMaterial = Math.min(breakNextLevelConf.Grouth, grouth) - breakLevelConf.Grouth;
                addAttrValue(addMaterials, breakNextLevelConf.GrouthMaterial, addGrouthMaterial);
                
                if (breakNextLevelConf.GrouthStarNum && breakNextLevelConf.GrouthStarId) {
                    var addGrouthStar = (Math.min(breakNextLevelConf.Grouth, grouth) - breakLevelConf.Grouth) * breakNextLevelConf.GrouthStarNum;
                    addAttrValue(addMaterials, breakNextLevelConf.GrouthStarId, addGrouthStar);
                }
            }

            // 炼魂/炼魂突破: 将魂, 武将卡, 神将碎片, 黑莲花
            var soulBreak1Max = gConfSoulBreak[1].Soul;
            addSoul = Math.floor(Math.min(role.soul, soulBreak1Max) * 18000);
            if (role.soul > soulBreak1Max) {
                addRoleCard = Math.floor((role.soul - soulBreak1Max) * 3 + 1);
            } else {
                addRoleCard = 1;
            }

            var soulBreakLevel = role.soul_break || 0;
            for (var level = 0; level < soulBreakLevel; ++level) {
                var breakLevelConf = gConfSoulBreak[level];
                addAttrValue(addMaterials, breakLevelConf.Material, breakLevelConf.MaterialNum);
            }

            // 觉醒: 神将碎片, 黑莲花, 舍利子
            var godLevel = role.god || 0;
            for (var level = 0; level < godLevel; ++level) {
                var godLevelConf = gConfGodLevel[level];
                addAttrValue(addMaterials, godLevelConf.Material, godLevelConf.MaterialNum);
            }
            
            // 坐骑: 马币
            if (role.ride) {
                var roleRide = role.ride;

                // 马鞭
                var whipMaterialCount = 0;
                for (var level = 1; level < roleRide.whip; ++level) {
                    whipMaterialCount += gConfHarnessLevel[level].Count; 
                }
                
                var whipMaterialId = gConfHarness.whip.Material;
                for (var id in gConfShop) {
                    if (whipMaterialId == gConfShop[id].Category3) {
                        addHorseCurrency += gConfShop[id].Price * whipMaterialCount;
                        break;
                    }
                }

                // 马鞍
                var seatMaterialCount = 0;
                for (var level = 1; level < roleRide.seat; ++level) {
                    seatMaterialCount += gConfHarnessLevel[level].Count; 
                }
                
                var seatMaterialId = gConfHarness.seat.Material;
                for (var id in gConfShop) {
                    if (seatMaterialId == gConfShop[id].Category3) {
                        addHorseCurrency += gConfShop[id].Price * seatMaterialCount;
                        break;
                    }
                }
            }

            // 羽翼: 仙羽, 魔羽, 黑莲花
            var wingLevel = role.wing || 0;
            if (wingLevel) {
                var wingConf = gConfWing[wingLevel];
                for (var i = 1; i <= 3; ++i) {
                    if (wingConf['KillReturnId'+i] && wingConf['KillReturnNum'+i]) {
                        addAttrValue(addMaterials, wingConf['KillReturnId'+i], wingConf['KillReturnNum'+i]);
                    }
                }
            }

            // 升星: 材料, 荣誉
            if (role.star) {
                for (var level = 0; level < role.star; ++level) {
                    var starConf = gConfRoleStar[soldierKind][level];
                    addHonor += (starConf.Honor || 0);

                    for (var i = 1; i <= 2; ++i) {
                        var returnId = starConf['Material'+i];
                        var returnNum = starConf['Num'+i];
                        if (returnId && returnNum) {
                            addAttrValue(addMaterials, returnId, returnNum);
                        }
                    }
                }
            }

            var horoscope = role.horoscope;
            if (horoscope) {
                var horoscopeGridConf = gConfHoroscopeGrid[roleConf.HoroscopeId];
                objectForEach(horoscope.grids, function(id, value) {
                    batchAwards.addFromCosts(horoscopeGridConf[id].OpenCosts);
                });
            }
        }

        for (var id in skillBooks) {
            addAttrValue(addMaterials, id, skillBooks[id]);
        }
        batchAwards.addMaterials(addMaterials);

        if (addSoul) {
            batchAwards.add('user', 'soul', addSoul);
        }

        if (addHonor) {
            batchAwards.add('user','honor', addHonor);
        }

        if (addRoleCard) {
            player.addRoleCard(rid, addRoleCard);
        }

        if (addHorseCurrency) {
            user.ride.horse_currency += addHorseCurrency; 
            player.markDirty('ride.horse_currency');
        }
        
    
        delete user.role[rid];
        player.markDelete('role.'+rid);
        
        resp.data.awards = player.addAwards(batchAwards.get());
        resp.data.cash = -costCash;
        resp.data.role_card = addRoleCard;
        resp.data.horse_currency = addHorseCurrency;
    }while(false);

    onHandled();
}

exports.take_off = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.id) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var id = +req.args.id;  // 装备ID

        var user = player.user;
        var equip = user.equip[id];
        if( !equip ) {
            resp.code = 1; resp.desc = 'no equip'; break;
        }

        var equipWeard = player.getEquipWeard();
        var weardRole = equipWeard[id];
        if( !weardRole ) {
            resp.code = 1; resp.desc = 'not wear'; break;
        }

        player.roleRemoveEquip(user.role[weardRole], equip);

        player.roleDirty = true;
    }while(false);
    
    onHandled();
}

exports.soul = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) || isNaN(req.args.card1) 
            || isNaN(req.args.card2) || isNaN(req.args.card3) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }


        var rid = +req.args.rid;
        var cards = [+req.args.card1, +req.args.card2, +req.args.card3];

        var user = player.user;
        if( user.status.level < gConfGlobal.RoleSoulOpenLevel ) {
            resp.code = 1; resp.desc = 'level not reach'; break;
        }

        var cardCounts = {};
        for( var i=0; i<cards.length; i++ ) {
            var card = cards[i];
            if( !cardCounts[card] ) {
                cardCounts[card] = 0;
            }
            cardCounts[card] += 1;
        }

        
        var role = user.role[rid];
        if( !role ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        if( gConfRole[rid].Quantity != 'orange' ) {
            resp.code = 1; resp.desc = 'not orange'; break;
        }
        
        var cardOK = true;
        for( var card in cardCounts ) {
            var count = cardCounts[card];
            var roleConf = gConfRole[card];
            if( !roleConf || roleConf.Quantity != 'orange' || !user.role_card[card] 
                || user.role_card[card] < count ) {
                cardOK = false;
                break;
            }
            
            if( (2 == role.soul_break) && ((+card != rid) || (count != 3)) ) {
                // 炼魄时需要3张与所炼英雄同样的卡牌
                cardOK = false;
                break;
            }
        }

        if( !cardOK ) {
            resp.code = 1; resp.desc = 'no more card'; break;
        }
    
        var soulLevel = role.soul || 0;
        var maxSoulLevel = gConfSoulBreak[role.soul_break].Soul;
        if( soulLevel >= maxSoulLevel || !gConfSoul[soulLevel+1] ) {
            resp.code = 1; resp.desc = 'up to max'; break;
        }

        for( var i=0; i<cards.length; i++ ) {
            var card = cards[i];
            user.role_card[card] -= 1;
            player.markDirty('role_card.'+card);
        }

        role.soul = soulLevel + 1;
        player.markDirty(util.format('role.%d.soul', rid));
        
        if( role.soul >= maxSoulLevel ) {
            gMessage.addSystemMessage('role_god'+(role.soul_break+1), [user.info.un, gConfRole[rid].Name]);
        }

        player.roleDirty = true;

        player.doEventRoleSoul(role);
    }while(false);
    
    onHandled();
}

exports.eat = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = +req.args.rid;

        var user = player.user;
        var role = user.role[rid];
        if( !role ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        if( role.level >= user.status.level ) {
            resp.code = 1; resp.desc = 'role reach max level'; break;
        }

        var num = 1;
        if( !isNaN(req.args.num) ) {
            num = Math.round(req.args.num);  
        }

        if( num <= 0 ) {
            resp.code = 1; resp.desc = 'invalid num'; break;
        }
        
        var materialId = 45;
        var materialNum = num;

        if( player.getInventory('material', materialId) < materialNum ) {
            resp.code = 1; resp.desc = 'not enough material 45'; break;
        }

        var levelConf = gConfLevel[user.status.level];
        if( !levelConf ) {
            resp.code = 1; resp.desc = 'level error'; break;
        }

        var gainXp = levelConf.TrainingXp;
        gainXp = Math.floor(num * gainXp * (gConfTraining[1].XpFactor2 / 100 + 1));

        var xp = role.xp + gainXp;
        var i = 1;
        for( i=1; i<1000; i++ ) {
            if( !gConfRoleLevel[i+1] || gConfRoleLevel[i].Xp > xp ) {
                break;
            }
        }

        if( i > user.status.level ) {
            gainXp = gConfRoleLevel[user.status.level].Xp - 1 - role.xp;
        }

        player.addRoleXp(role, gainXp);
        player.roleDirty = true;

        player.addInventory('material', materialId, -materialNum);

        resp.data.xp = role.xp;
        resp.data.level = role.level;
        
    }while(false);
    
    onHandled();
}

exports.awake = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var user = player.user;
        if( user.status.level < gConfGlobal.AwakeOpenLevelLimit ) {
            resp.code = 1; resp.desc = 'level not reach'; break;
        }

        var rid = parseInt(req.args.rid);
        var roleConf = gConfRole[rid];

        var role = user.role[rid];
        if( !role ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }
        
        var nextGodLevel = role.god + 1;
        if( !gConfGodLevel[nextGodLevel] ) {
            resp.code = 1; resp.desc = 'up to max'; break;
        }

        if (nextGodLevel >= GodDefines.lord && !gConfSwitch.RoleLord) {
            resp.code = 1; resp.desc = 'lord not open'; break;
        }

        if( !roleConf['GodUrl'+nextGodLevel] ) {
            resp.code = 1; resp.desc = 'can not awake'; break;
        }

        var godLevelConf = gConfGodLevel[role.god];
        if( role.soul < godLevelConf.Soul ) {
            resp.code = 1; resp.desc = 'soul level not reach'; break;
        }

        if( godLevelConf.Grouth && (role.grouth < godLevelConf.Grouth) ) {
            resp.code = 1; resp.desc = 'grouth not reach'; break;
        }

        if( (roleConf['OpenDays'+nextGodLevel] * 3600 + gConfGlobalServer.ServerStartTime) > common.getTime() ) {
            resp.code = 1; resp.desc = 'time limit'; break;
        }

        var materialId = godLevelConf.Material;
        var materialNum = godLevelConf.MaterialNum;
        var materialCost = player.addInventory('material', materialId, -materialNum);
        if (!materialCost) {
            resp.code = 1; resp.desc = 'not enough material'; break; 
        }
        
        role.god += 1;
        player.markDirty(util.format('role.%d.god', rid));
        player.roleDirty = true;

        if (gConfGodLevel[role.god].SpSkill) {
            var spSkillFlag = +(gConfGodLevel[role.god].SpSkill);
            if (1 == spSkillFlag && roleConf.SpecialSkill) {
                role.skill_levels[roleConf.SpecialSkill] = 1;
                player.markDirty(util.format('role.%d.skill_levels', rid));
            } else if (2 == spSkillFlag && roleConf.SpecialSkill2) {
                role.skill_levels[roleConf.SpecialSkill2] = 1;
                player.markDirty(util.format('role.%d.skill_levels', rid));
            }
        }

        if( 1 == role.god ) {
            // 第一次觉醒时,增加坐骑
            var horseUniqId = player.addHorse(gConfGlobal.InitalHorseId);
            player.addRoleRide(rid, horseUniqId);

            var retHorse = {};
            retHorse[horseUniqId] = user.ride.horse[horseUniqId];
            resp.data.horse = retHorse; 

            // 增加初始羽翼
            player.addRoleWing(rid, gConfGlobal.InitialWingLevel);
            resp.data.wing = gConfGlobal.InitialWingLevel;
        }

        gMessage.addSystemMessage('role_awake'+role.god, [user.info.un, roleConf.Name]);

        resp.data.material = materialCost;
        resp.data.god = role.god;

        player.doEventRoleAwake(role);
    }while(false);
    
    onHandled();
}

exports.open_all_skill = function(player, req, resp, onHandled) {
    do {
        var user = player.user;
        var dirtyRoles = {};

        for( var rid in user.role ) {
            var role = user.role[rid];
            var soldierKind = gConfRole[rid].Soldier;
            var skillConfs = gConfSkill[soldierKind]
            var skill_levels = role.skill_levels;
            for( var skillId in skillConfs ) {
                if( skill_levels[skillId] ) continue;

                var skillConf = skillConfs[skillId];
                if( role.level >= skillConf.RoleLevel ) {
                    skill_levels[skillId] = 1;
                    dirtyRoles[rid] = 1;
                }
            }
        }

        if( Object.keys(dirtyRoles).length == 0 ) {
            resp.code = 1; resp.desc = 'no new skill'; break;
        }

        var skills = {};
        for( var rid in dirtyRoles ) {
            skills[rid] = user.role[rid].skill_levels;
            player.markDirty(util.format('role.%d.skill_levels', rid));
        }

        player.roleDirty = true;
        player.doTask('UpgradeSkill','all');

        resp.data.skills = skills;

    }while(false);

    onHandled();
}

exports.grouth_break = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) ) {
            resp.code = 1; resp.desc = 'no valid args: rid'; break;
        }

        var rid = +req.args.rid;

        var user = player.user;
        var role = user.role[rid];
        if( !role ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        if( !gConfGrouthBreak[role.grouth_break+1] ) {
            resp.code = 1; resp.desc = 'up to max'; break;
        }

        var breakConf = gConfGrouthBreak[role.grouth_break];
        if( role.grouth < breakConf.Grouth ) {
            resp.code = 1; resp.desc = 'grouth not reach'; break;
        }

        
        var materialId = breakConf.Material;
        var materialNum = breakConf.MaterialNum;

        if( 0 == role.grouth_break ) {
            // 1次培养突破,判断技能需求
            var fullSkills = 0;
            for( var i=1; i<=5; i++ ) {
                if( (role.skill_levels[i] || 0) >= 10 ) {
                    fullSkills += 1;
                }
            }

            if( fullSkills < breakConf.FullSkill ) {
                resp.code = 1; resp.desc = 'not enough full skill'; break;
            }
        } else if( 1 == role.grouth_break ) {
            if( role.god && role.god < breakConf.FullSkill ) {
                resp.code = 1; resp.desc = 'god level not reach'; break;
            }
        }
        
        var materialCost = player.addInventory('material', materialId, -materialNum);
        if (!materialCost) {
            resp.code = 1; resp.desc = 'not enough material'; break;
        }

        role.grouth_break += 1;
        player.markDirty(util.format('role.%d.grouth_break', rid));
        player.roleDirty = true;

        gMessage.addSystemMessage('role_grouth_break', [user.info.un, gConfRole[rid].Name]);

        resp.data.grouth_break = role.grouth_break;
        resp.data.material = materialCost;

    }while(false);

    onHandled();
}

exports.kill_special_role = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = +req.args.rid;
        var isCash = req.args.cash ? true : false;

        if (rid == gDefaultRoleId) {
            resp.code = 1; resp.desc = 'is default role'; break;
        }

        var user = player.user;
        var role = user.role[rid];
        if( !role ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var inTeam = false;
        for( var pos in user.team ) {
            if( rid == user.team[pos] ) {
                inTeam = true;
                break;
            }
        }

        if( inTeam ) {
            resp.code = 1; resp.desc = 'in team'; break;
        }

        if( isCash && (gConfRole[rid].Quantity != 'orange') ) {
            resp.code = 1; resp.desc = 'no cash kill'; break;
        }
        
        if( isCash && !player.costResource('cash', gConfGlobal.KillRoleCostCash, rid) ) {
            resp.code = 1; resp.desc = 'no enough cash'; break;
        }
        
        var soldierKind = gConfRole[rid].Soldier;
        var awards = [];  

        // 技能书
        var skillBooks = {};
        var skills = role.skill_levels;
        for( var skillId in skills ) {
            var skillLevel = skills[skillId];
            var skillKind = gGetSkillKind(soldierKind, skillId);
            var skillConf = gConfSkill[skillKind][skillId];

            for( var i=2; i<=skillLevel; i++ ) {
                var materialId = skillConf.Material;
                var materialNum = skillConf['MaterialNum'+i];
                if( !skillBooks[materialId] ) {
                    skillBooks[materialId] = 0;
                }
                skillBooks[materialId] += materialNum;
            }
        }

        var costCash = 0;
        if( !isCash ) {
            for( var materialId in skillBooks ) {
                var returnBack = Math.floor(skillBooks[materialId]/2);
                skillBooks[materialId] = returnBack;
            }
        }else {
            costCash = gConfGlobal.KillRoleCostCash;
            for( var materialId in skillBooks ) {
                var returnBack = Math.floor(skillBooks[materialId] * 0.8);
                skillBooks[materialId] = returnBack;
            }
        
            // 将魂 
            var addSoul = Math.floor(3 * 6000 * role.soul * 0.8);
            if( addSoul ) {
                awards.push(['user','soul', addSoul]);
            }
            
            // 洗髓丹
            var addRefineMaterial = Math.ceil(0.8 * (role.attack + role.defence + role.mdefence) / 5); 
            if( addRefineMaterial ) {
                awards.push(['material', gConfGlobal.RoleRefineMaterial, addRefineMaterial]);
            }

            // 神将碎片 黑莲花
            var godLevel = role.god || 0;
            for( var level in gConfGodLevel ) {
                if( godLevel <= level ) continue;
                var godLevelConf = gConfGodLevel[level];
                if( !godLevelConf.Material ) continue;
                var materialNum = Math.floor(godLevelConf.MaterialNum * 0.8);
                if( !materialNum ) continue;
                awards.push(['material', godLevelConf.Material, materialNum]);
            }
            
            var soulBreakLevel = role.soul_break || 0;
            for( var level in gConfSoulBreak ) {
                if( soulBreakLevel <= level ) continue;
                var soulBreakConf = gConfSoulBreak[level];
                if( !soulBreakConf.Material ) continue;
                var materialNum = Math.floor(soulBreakConf.MaterialNum * 0.8);
                if( !materialNum ) continue;
                awards.push(['material', soulBreakConf.Material, materialNum]);
            }

            var grouthBreakLevel = role.grouth_break || 0;
            for( var level in gConfGrouthBreak ) {
                if( grouthBreakLevel <= level ) continue;
                var grouthBreakConf = gConfGrouthBreak[level];
                if( !grouthBreakConf.Material ) continue;
                var materialNum = Math.floor(grouthBreakConf.MaterialNum * 0.8);
                if( !materialNum ) continue;
                awards.push(['material', grouthBreakConf.Material, materialNum]);
            }
            
            // 兵符
            var soldierLevel = role.soldier_level;
            var soldierConf = gConfSoldier[soldierKind];
            for( var i = 1; i <= soldierLevel; i++ ) {
                var addSoldierMaterial = Math.floor(soldierConf[i].MaterialNum * 0.8); 
                if( !addSoldierMaterial ) continue;
                awards.push(['material', soldierConf[i].Material, addSoldierMaterial]);
            }

            // 培养丹
            var grouth = role.grouth || 0;
            var grouthConf = gConfGrouth.orange;
            for( var level in soldierConf ) {
                if( soldierLevel < level )  continue;
                var levelMinGrouth = grouthConf['Min'+level] || gConfRole[rid].Grouth;
                var levelMaxGrouth = grouthConf['Max'+level];
                
                var addGrouthMaterial = 0;
                if( (grouth > levelMinGrouth) && (grouth < levelMaxGrouth) ) {
                    addGrouthMaterial = grouth - levelMinGrouth; 
                }else if( grouth >= levelMaxGrouth ) {
                    addGrouthMaterial = levelMaxGrouth - levelMinGrouth; 
                }
                
                if( addGrouthMaterial ) {
                    awards.push(['material', soldierConf[level].GrouthMaterial, addGrouthMaterial]);
                }
            }

            // 突破后的培养
            for( var level in gConfGrouthBreak ) {
                if( grouthBreakLevel < level ) continue;
                var grouthBreakConf = gConfGrouthBreak[level];
                if( !grouthBreakConf.GrouthMaterial ) continue;

                var levelMinGrouth = gConfGrouthBreak[level - 1].Grouth;
                var levelMaxGrouth = grouthBreakConf.Grouth;
                var addGrouthMaterial = 0;
                if( grouth >= levelMaxGrouth ) {
                    addGrouthMaterial = levelMaxGrouth - levelMinGrouth;
                }else {
                    addGrouthMaterial = grouth - levelMinGrouth; 
                }

                if( addGrouthMaterial ) {
                    awards.push(['material', grouthBreakConf.GrouthMaterial, addGrouthMaterial]);
                }
            }

            // 马币 
            if( role.ride ) {
                var roleRide = role.ride;
                var addHorseCurrency = 0;

                // 马鞭
                var whipMaterialCount = 0;
                for( var level = 1; level < roleRide.whip; level++ ) {
                    whipMaterialCount += gConfHarnessLevel[level].Count; 
                }
                
                var whipMaterialId = gConfHarness.whip.Material;
                for( var id in gConfShop ) {
                    if( whipMaterialId != gConfShop[id].Category3 ) continue;
                    addHorseCurrency += gConfShop[id].Price * whipMaterialCount;
                    break;
                }

                // 马鞍
                var seatMaterialCount = 0;
                for( var level = 1; level < roleRide.seat; level++ ) {
                    seatMaterialCount += gConfHarnessLevel[level].Count; 
                }
                
                var seatMaterialId = gConfHarness.seat.Material;
                for( var id in gConfShop ) {
                    if( seatMaterialId != gConfShop[id].Category3 ) continue;
                    addHorseCurrency += gConfShop[id].Price * seatMaterialCount;
                    break;
                }
                
                var retHorseCurrency = Math.floor(addHorseCurrency * 0.8);
                user.ride.horse_currency += retHorseCurrency; 
                player.markDirty('ride.horse_currency'); 

                resp.data.horse_currency = retHorseCurrency;
            }
        }
        
        for( var id in skillBooks ) {
            if( !skillBooks[id] ) continue;
            awards.push(['material', +id, +skillBooks[id]]); 
        }
    
        delete user.role[rid];
        player.markDelete('role.'+rid);
        
        resp.data.awards = player.addAwards(awards);
        resp.data.cash = -costCash;
    }while(false);

    onHandled();
}

exports.soul_break = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) ) {
            resp.code = 1; resp.desc = 'no valid args: rid'; break;
        }

        var rid = +req.args.rid;

        var user = player.user;
        var role = user.role[rid];
        if( !role ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        if( !gConfSoulBreak[role.soul_break+1] ) {
            resp.code = 1; resp.desc = 'up to max'; break;
        }

        var breakConf = gConfSoulBreak[role.soul_break];
        if( role.soul < breakConf.Soul ) {
            resp.code = 1; resp.desc = 'soul not reach'; break;
        }

        if( role.god < breakConf.God ) {
            resp.code = 1; resp.desc = 'god not reach'; break;
        }

        if (role.grouth < breakConf.Grouth) {
            resp.code = 1; resp.desc = 'grouth not reach'; break;
        }

        var materialId = breakConf.Material;
        var materialNum = breakConf.MaterialNum;
        var materialCost = player.addInventory('material', materialId, -materialNum);
        if (!materialCost) {
            resp.code = 1; resp.desc = 'not enough material'; break;
        }
        
        role.soul_break += 1;
        player.markDirty(util.format('role.%d.soul_break', rid));
        player.roleDirty = true;

        if ( role.soul_break <= 1 ) {
            gMessage.addSystemMessage('role_soul_break', [user.info.un, gConfRole[rid].Name]);
        } else {
            gMessage.addSystemMessage('role_soul_break2', [user.info.un, gConfRole[rid].Name]);
        }

        resp.data.soul_break = role.soul_break;
        resp.data.material = materialCost;

    }while(false);

    onHandled();
}

exports.change_horse = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) || isNaN(req.args.id) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = Math.floor(req.args.rid);
        var horseUniqId = Math.floor(req.args.id);

        var user = player.user;
        var roles = user.role;
        var role = roles[rid];
        var ride = user.ride;
        if( !role || !role.ride ) {
            resp.code = 1; resp.desc = 'no role ride'; break;
        }
         
        var horse = ride.horse[horseUniqId];
        if( !horse ) {
            resp.code = 1; resp.desc = 'id error'; break;
        }
        
        var isHorseInUse = false;
        for( var id in roles ) {
            if( !roles[id].ride || roles[id].ride.horse != horseUniqId ) continue;
            isHorseInUse = true; break;
        }

        if( isHorseInUse ) {
            resp.code = 1; resp.desc = 'in use'; break;
        }
        
        role.ride.horse =  horseUniqId,
        player.markDirty(util.format('role.%d.ride.horse', rid));

        player.roleDirty = true;

    }while(false);

    onHandled();
}

exports.upgrade_ride = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) || !('horse'== req.args.type || 
            'seat' == req.args.type || 'whip' == req.args.type) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = Math.floor(req.args.rid);
        var type = req.args.type;

        var user = player.user;
        var role = user.role[rid];
        if( !role ) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        if( !role.ride ) {
            resp.code = 1; resp.desc = 'no role ride'; break;
        }
        
        var userRide = user.ride;

        var horseUniqId = role.ride.horse;
        var upgradeHorse = userRide.horse[horseUniqId];
        var upgradeHorseId = upgradeHorse.horse_id;
        if( 'horse' == type ) {
            if( !gConfHorseLevel[upgradeHorse.level + 1] ) {
                resp.code = 1; resp.desc = 'level to max'; break;
            }

            //  正在被使用的马匹
            var usedHorses = {};
            var roles = user.role;
            for( var id in roles ) {
                var role = roles[id];
                if( !role.ride ) continue; 

                usedHorses[role.ride.horse] = 1;
            }

            var sameIdHorses = {};
            var horses = user.ride.horse;
            for( var id in horses ) {
                if( (id in usedHorses)  || 
                    (horses[id].horse_id != upgradeHorseId ) )  continue;
                sameIdHorses[id] = 1;
            }
            
            var costHorseCount = gConfHorseLevel[upgradeHorse.level].Count;
            if( Object.keys(sameIdHorses).length < costHorseCount ) {
                resp.code = 1; resp.desc = 'not enough horse'; break;
            }
            
            // 将同一个ID的马匹按等级从小到大排列
            var sortedSameIdHorses = [];
            for( var id in sameIdHorses ) {
                var horse = horses[id];
                sortedSameIdHorses.push({'id' : id, 'level': horse.level}); 
            }

            sortedSameIdHorses = sortedSameIdHorses.sort(function(a, b) { return a.level - b.level; });

            var costHorses = {};
            for( var i = 0; i < costHorseCount; i++) {
                var id = sortedSameIdHorses[i].id;
                delete horses[id];  
                player.markDelete(util.format('ride.horse.%d', id));
                costHorses[id] = 1;
            }

            upgradeHorse.level += 1;
            player.markDirty(util.format('ride.horse.%d.level', horseUniqId));
            
            if( !gConfHorseLevel[upgradeHorse.level + 1] ) {
                gMessage.addSystemMessage('upgrade_ride', [user.info.un, gConfHorse[upgradeHorseId].Name]);
            }

            resp.data.horse = costHorses;
        }else {
            var harnessLevel = role.ride[type];
            if( !gConfHarnessLevel[harnessLevel + 1] ) {
                resp.code = 1; resp.desc = 'level to max'; break;
            }
            
            var materialId = gConfHarness[type].Material;
            var costMaterialCount = gConfHarnessLevel[harnessLevel].Count;
            if( !player.addInventory('material', materialId, -costMaterialCount) ) {
                resp.code = 1; resp.desc = 'not enough material'; break;
            }

            role.ride[type] += 1;
            player.markDirty(util.format('role.%d.ride.%s', rid, type));
            var materialCost = {};
            materialCost[materialId] = -costMaterialCount;

            if( !gConfHarnessLevel[role.ride[type] + 1] ) {
                gMessage.addSystemMessage('upgrade_ride', [user.info.un, gConfHarness[type].Name]);
            }

            resp.data.material = materialCost;
        }
        
        player.roleDirty = true;
    }while(false);

    onHandled();
}

exports.sell_horse = function(player, req, resp, onHandled) {
    do {
        if( (req.args.id && isNaN(req.args.id)) ||
            (req.args.quantity && !(req.args.quantity in horseQuantityMap)) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }
        
        var sellHorseId = req.args.id ? Math.floor(req.args.id) : 0;
        var isSellAll = req.args.all ? true : false;
        var sellHorseQuantity = req.args.quantity ? req.args.quantity : '';
        
        var user = player.user;
        var roles = user.role;
        var usedHorses = {};
        for( var id in roles ) {
            var role = roles[id];
            if( !role.ride ) continue; 

            usedHorses[role.ride.horse] = 1;
        }
        
        var ride = user.ride; 
        var horses = ride.horse;
        var sellHorses = {};
        if( sellHorseId ) {
            if( !(sellHorseId in horses) ) {
                resp.code = 1; resp.desc = 'id error'; break;
            } 

            if( sellHorseId in usedHorses ) {
                resp.code = 1; resp.desc = 'in use'; break;
            }

            sellHorses[sellHorseId] = 1;   

        }else if( sellHorseQuantity ) {
            for( var id in horses ) {
                if( (id in usedHorses) ||
                    (sellHorseQuantity != gConfHorse[horses[id].horse_id].Quantity) )
                    continue;

                sellHorses[id] = 1;
            } 
        }else if( isSellAll ) {
            for( var id in horses ) {
                if( id in usedHorses )  continue;
                sellHorses[id] = 1;
            } 
        }
        
        if( Object.keys(sellHorses).length <=0 ) {
            resp.code = 1; resp.desc = 'no horse in sell'; break;
        }

        var horseCurrency = 0;
        for( var id in horses ) {
            if( !(id in  sellHorses) ) continue;
            var horse = horses[id];
            var horseQuantity = gConfHorse[horse.horse_id].Quantity;
            horseCurrency += gConfHorseSell[horse.level]['Quantity'+horseQuantity.capWord()];
            delete horses[id]; 
            player.markDelete(util.format('ride.horse.%d', id));
        }
        
        ride.horse_currency += horseCurrency;
        player.markDirty('ride.horse_currency');
        
        resp.data.horse_currency = horseCurrency;
        resp.data.sell_horse = sellHorses;

    }while(false);

    onHandled();
}

exports.catch_horse = function(player, req, resp, onHandled) {
    do {
        if( !('gold' == req.args.type || 'material' == req.args.type) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }
        
        var catchType = req.args.type;
        var user = player.user;

        if (common.getDayPassed(gConfGlobalServer.ServerStartTime) < gConfGlobal.RoleHorseOpenDays ||
            user.status.level < gConfGlobal.RoleHorseOpenLevel) {
            resp.code = 1; resp.desc = 'no open'; break;
        }

        var ride = user.ride;
        if( !ride ) {
            resp.code = 1; resp.desc = 'no ride'; break;
        }

        if( Object.keys(ride.horse).length >= gConfGlobal.OwnHorseLimit ) {
            resp.code = 1; resp.desc = 'to max'; break;
        }

        var weightType = '';
        if( 'material' == catchType ) {
            var materialId = gConfGlobal.CatchHorseMaterialId;
            var costNum = gConfGlobal.CatchHorseMaterialNum;
            if( !player.addInventory('material', materialId, -costNum) ) {
                resp.code = 1; resp.desc = 'not enough material'; break;
            } 

            weightType = 'MaterialWeight';
            var costMaterial = {};
            costMaterial[materialId] = -costNum;
            resp.data.material = costMaterial; 
        }else if( 'gold' == catchType ) {
            var goldCatchCount = ride.gold + 1;
            var costGold = Math.pow(2, ride.gold) * gConfGlobal.CatchHorseGoldCost;
            if( costGold > gConfGlobal.CatchHorseGoldCostMax ) {
                costGold =  gConfGlobal.CatchHorseGoldCostMax;
            }

            if( !player.costResource('gold', costGold) ) {
                resp.code = 1; resp.desc = 'not enough gold'; break;
            }

            if( !gConfHorse[1]['GoldWeight'+goldCatchCount] ) {
                weightType = 'GoldWeightFinal' 
            }else {
                weightType = 'GoldWeight' + goldCatchCount;
            }
            
            ride.gold = goldCatchCount;
            player.markDirty('ride.gold');
            
            resp.data.gold = -costGold;
        }

        var weights = {};
        for( var id in gConfHorse ) {
            weights[id] = gConfHorse[id][weightType]; 
        }

        var horseId = +common.wRand(weights);
        var horseUniqId = player.addHorse(horseId);
        
        if( 'orange' == gConfHorse[horseId].Quantity ) {
            gMessage.addSystemMessage('catch_horse', [user.info.un, gConfHorse[horseId].Name]);
        }

        var retHorse = {};
        retHorse[horseUniqId] = {
            'horse_id' : horseId,
            'level' : 1,
        };
         
        resp.data.horse = retHorse;
    }while(false);

    onHandled();
}

exports.exchange_horse = function(player, req, resp, onHandled) {
    var user = player.user;
    do {
        if( isNaN(req.args.horse) || isNaN(req.args.num) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        if (common.getDayPassed(gConfGlobalServer.ServerStartTime) < gConfGlobal.RoleHorseOpenDays ||
            user.status.level < gConfGlobal.RoleHorseOpenLevel) {
            resp.code = 1; resp.desc = 'no open'; break;
        }

        var exchangeHorseId = Math.floor(req.args.horse);
        var num = Math.floor(req.args.num);

        if( num <=0 ) {
            resp.code = 1; resp.desc = 'num error'; break;
        }

        var ride = user.ride;
        if( !ride ) {
            resp.code = 1; resp.desc = 'no ride'; break;
        }

        if( num + Object.keys(ride.horse).length > gConfGlobal.OwnHorseLimit ) {
            resp.code = 1; resp.desc = 'to max'; break;
        }
        
        var horseConf = gConfHorse[exchangeHorseId];
        if( !horseConf || !horseConf.ExchangeHorseCurrency ) {
            resp.code = 1; resp.desc = 'horse error'; break;
        }
        
        var costHorseCurrency = num * horseConf.ExchangeHorseCurrency;
        if( ride.horse_currency < costHorseCurrency ) {
            resp.code = 1; resp.desc = 'not enough horse currency'; break;
        }
        
        ride.horse_currency -= costHorseCurrency;
        player.markDirty('ride.horse_currency');
        
        var retHorses = {};
        for( var i = 0; i < num; i++ ) {
            var horseUniqId = player.addHorse(exchangeHorseId);
            retHorses[horseUniqId] = {
                'horse_id' : exchangeHorseId,
                'level' : 1,
            };
        }
         
        resp.data.horse = retHorses;
        resp.data.horse_currency = -costHorseCurrency;
    }while(false);

    onHandled();
}

exports.upgrade_wing = function(player, req, resp, onHandled) {
    do {
        if( isNaN(req.args.rid) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var rid = Math.floor(req.args.rid);

        var now = common.getTime();
        var dayPassed = common.getDayPassed( gConfGlobalServer.ServerStartTime );
        if( dayPassed < gConfGlobal.WingOpenDays ) {
            resp.code = 1; resp.desc = 'not open'; break;
        }

        var user = player.user;
        if( user.status.level < gConfGlobal.WingOpenLevel ) {
            resp.code = 1; resp.desc = 'level not reach'; break;
        }

        var roleConf = gConfRole[rid];
        if (!roleConf) {
            resp.code = 1; resp.desc = 'no role'; break;
        }

        var role = user.role[rid]; 
        if( !(role && role.god && role.wing) ) {
            resp.code = 1; resp.desc = 'can not upgrade'; break;
        }
    
        var nextWingLevel = role.wing + 1;
        var nextWingConf = gConfWing[nextWingLevel];
        if( !nextWingConf ) {
            resp.code = 1; resp.desc = 'to max'; break;
        }

        if( nextWingConf.OpenDay ) {
            if( dayPassed < nextWingConf.OpenDay ) {
                resp.code = 1; resp.desc = 'wing not open'; break;
            }
        } 

        var wingStage = Math.floor((nextWingLevel-1)/wingDefines.stageMaxLevel);
        if (wingDefines.stage.demon == wingStage) {
            if (!gConfSwitch.RoleDemonWing) {
                resp.code = 1; resp.desc = 'demon wing not open'; break;
            }
        }

        var wingConf = gConfWing[role.wing];
        var materialId = wingConf.Material;
        var materialNum = wingConf.Num;
        var retMaterial = player.addInventory('material', materialId, -materialNum);
        if (!retMaterial) {
            resp.code = 1; resp.desc = 'material not enough'; break;
        }


        role.wing = nextWingLevel;
        player.markDirty(util.format('role.%d.wing',rid));
        player.roleDirty = true;
        if (wingDefines.stage.demon == wingStage) {
            var wingStageMod = nextWingLevel%wingDefines.stageMaxLevel;
            if (1 == wingStageMod) {
                gMessage.addSystemMessage('role_wing_demon', [user.info.un, roleConf.Name]);
            } else if (0 == wingStageMod) {
                gMessage.addSystemMessage('role_wing_demonmax', [user.info.un, roleConf.Name]);
            }
        }
        
        var retWing = {};
        retWing[rid] = nextWingLevel;
        
        resp.data.material = retMaterial;
        resp.data.wing = retWing;

        player.doEventRoleUpgradeWing(role);
    }while(false);

    onHandled();
}

exports.merge_feather = function(player, req, resp, onHandled) {
    do {
        var now = common.getTime(); 
        if( common.getDayPassed(gConfGlobalServer.ServerStartTime) < gConfGlobal.WingOpenDays ) {
            resp.code = 1; resp.desc = 'not open'; break;
        }

        if( isNaN(req.args.level) || isNaN(req.args.count) ) {
            resp.code = 1; resp.desc = 'no valid args'; break;
        }

        var level = Math.floor(req.args.level);
        var count = Math.floor(req.args.count);

        var user = player.user;
        if( user.status.level < gConfGlobal.WingOpenLevel ) {
            resp.code = 1; resp.desc = 'level not reach'; break;
        }

        var wingConf = gConfWing[level];
        if( !wingConf || count <= 0 ) {
            resp.code = 1; resp.desc = 'args error'; break;
        } 

        var nextLevel = level + 1; 
        if (nextLevel%wingDefines.stageMaxLevel <=1) {
            resp.code = 1;
            resp.desc = 'level error';
            break;
        }

        var nextWingConf = gConfWing[nextLevel]; 
        var newMaterial =  nextWingConf ? nextWingConf.Material : 0;
        if ( !nextWingConf || !newMaterial ) {
            resp.code = 1; resp.desc = 'to max'; break;
        }

        var costNum = gConfGlobal.WingMakeNum * count;
        if( !player.addInventory('material', wingConf.Material, -costNum ) ) {
            resp.code = 1; resp.desc = 'material not enough'; break;
        }

        player.addInventory('material', newMaterial, count);

        resp.data.feather = newMaterial;
    }while(false);

    onHandled();
}

exports.upgrade_star = function(player, req, resp, onHandled) {
    do {
        if (isNaN(req.args.rid)) {
            resp.code = 1; resp.desc = 'invalid args'; break;
        }

        var rid = parseInt(req.args.rid);

        var user = player.user;
        if (user.status.level < gConfGlobal.RoleStarOpenLevel) {
            resp.code = 1; resp.desc = 'level not reach'; break;
        }

        var role = user.role[rid]; 
        var roleConf = gConfRole[rid];
        if (!role || !roleConf) {
            resp.code = 1; resp.desc = 'invalid role'; break;
        }

        var soldierStarConfs = gConfRoleStar[roleConf.Soldier];
        var curStar = role.star || 0;
        if (!soldierStarConfs[curStar + 1]) {
            resp.code = 1; resp.desc = 'top star level'; break;
        }

        var starConf = soldierStarConfs[curStar];
        var costHonor = starConf.Honor;
        if (costHonor && user.status.honor < costHonor) {
            resp.code = 1; resp.desc = 'soul not enough'; break;
        }

        if (player.getInventory('material', starConf.Material1) < starConf.Num1 ||
            player.getInventory('material', starConf.Material2) < starConf.Num2) {
            resp.code = 1; resp.desc = 'not enough material'; break;
        }

        var costMaterials = {};
        costMaterials[starConf.Material1] = -starConf.Num1;
        costMaterials[starConf.Material2] = -starConf.Num2;
        for (var id in costMaterials) {
            player.addInventory('material', id, costMaterials[id]);
        }

        if (costHonor) {
            player.costResource('honor', costHonor);
        }

        role.star = curStar + 1;
        player.markDirty(util.format('role.%d.star', rid));
        player.roleDirty = true;

        resp.data.material = costMaterials;
        resp.data.honor = -costHonor;

        player.doEventRoleUpgradeStar(role);
    } while (false);
    
    onHandled && onHandled();
}

exports.horoscope_grid_open = function(player, req, resp, onHandled) {
    var user = player.user;
    do {
        if (user.status.level < gConfGlobal.HoroscopeOpenLevel) {
            resp.code = 1; resp.desc = 'not open'; break;
        }

        if (isNaN(req.args.rid) || isNaN(req.args.gridId)) {
            resp.code = 1; resp.desc = 'invalid args'; break;
        }

        var rid = parseInt(req.args.rid);
        var gridId = parseInt(req.args.gridId);

        var role = user.role[rid];
        if (!role) {
            resp.code = 1; resp.desc = 'invalid rid'; break;
        }

        if (role.god < 1) {
            resp.code = 1; resp.desc = 'role.god < 1'; break;
        }

        var roleConf = gConfRole[rid];
        if (!roleConf.HoroscopeId) {
            resp.code = 1; resp.desc = 'no horoscope'; break;
        }

        var horoscopeId = roleConf.HoroscopeId;
        var horoscopeGridConf = gConfHoroscopeGrid[horoscopeId];
        var gridConf = horoscopeGridConf[gridId];
        if (!gridConf) {
            resp.code = 1; resp.desc = 'invalid grid'; break;
        }
        
        var horoscope = role.horoscope;
        if (!horoscope) {
            horoscope = {grids:{}, attrs:{}};
            role.horoscope = horoscope;
        }

        if (horoscope.grids.hasOwnProperty(gridId)) {
            resp.code = 1; resp.desc = 'opened'; break;
        }

        if (gridId != roleConf.StartGridId) {
            // 判断周边是否有已开启的格子
            var sideOpened = false;
            var posX = gridConf.PosX;
            var posY = gridConf.PosY;
            var horoscopeGridPosConf = gConfHoroscopeGridPos[horoscopeId];
            for (var x = -1; x <= 1; ++x) {
                for (var y = -1; y <= 1; ++y) {
					var rowGridConf = horoscopeGridPosConf[posX + x];
					if (rowGridConf) {
						var sideGridConf = rowGridConf[posY + y];
                    	if (sideGridConf) {
                    	    if (horoscope.grids.hasOwnProperty(sideGridConf.GridId)) {
                    	        sideOpened = true; break;
                    	    }
                    	}
					}
                }
                if (sideOpened) break;
            }

            if (!sideOpened) {
                resp.code = 1; resp.desc = 'side not open'; break;
            }
        }

        var costs = player.addCosts(gridConf.OpenCosts);
        if (0 == costs.length) {
            resp.code = 1; resp.desc = 'cost not enough'; break;
        }

        var effectConf = gConfHoroscopeEffect[gridConf.EffectId];
        horoscope.grids[gridId] = effectConf.Bind ? 0 : -1;
        player.calculateRoleHoroscopeAttrs(role);
        player.markDirty(util.format('role.%d.horoscope', rid));
        player.checkEvent("HoroscopeSum");
        player.roleDirty = true;

        resp.data.costs = costs;
        resp.data.horoscope = role.horoscope;
    } while (false);

    onHandled && onHandled();
}

exports.horoscope_grid_bind = function(player, req, resp, onHandled) {
    var user = player.user;
    do {
        if (user.status.level < gConfGlobal.HoroscopeOpenLevel) {
            resp.code = 1; resp.desc = 'not open'; break;
        }

        if (isNaN(req.args.rid) || isNaN(req.args.gridId) || isNaN(req.args.pet)) {
            resp.code = 1; resp.desc = 'invalid args'; break;
        }

        var rid = parseInt(req.args.rid);
        var gridId = parseInt(req.args.gridId);
        var petLevel = parseInt(req.args.pet);
		
        var pet = user.pet;
        if (!pet || (petLevel < 0) || (petLevel > pet.level)) {
            resp.code = 1; resp.desc = 'invalid pet'; break;
        }

        var roles = user.role;
        var role = roles[rid];
        if (!role) {
            resp.code = 1; resp.desc = 'invalid rid'; break;
        }

        var horoscope = role.horoscope;
        if (!horoscope) {
            resp.code = 1; resp.desc = 'not start'; break;
        }

        var horoscopeId = gConfRole[rid].HoroscopeId;
        if (!horoscopeId) {
            resp.code = 1; resp.desc = 'no horoscope'; break;
        }

        var grids = horoscope.grids;
        if (!grids || !grids.hasOwnProperty(gridId)) {
            resp.code = 1; resp.desc = 'grid not open'; break;
        }

        var gridConf = gConfHoroscopeGrid[horoscopeId][gridId];
        if (!gridConf) {
            resp.code = 1; resp.desc = 'invalid grid'; break;
        }

        var gridStatus = grids[gridId];
        if (!petLevel) { //解绑
            if (!gridStatus) {
                resp.code = 1; resp.desc = 'not bound'; break;
            }
        } else { //绑定
            if (gridStatus) {
                resp.code = 1; resp.desc = 'grid bound'; break;
            }

            var effectConf = gConfHoroscopeEffect[gridConf.EffectId];
            if (!effectConf || !effectConf.Bind) {
                resp.code = 1; resp.desc = 'can not bind'; break;
            }

			var petBoundRoleId = 0; //神兽已经绑定的武将ID;
            for (var forRid in roles) {
				var forRole = roles[forRid];
                var roleHoroscope = forRole.horoscope;
                if (!roleHoroscope) {
                    continue;
                }

				var roleHoroscopeGrids = roleHoroscope.grids;
                var gridArr = Object.keys(roleHoroscopeGrids);
                for (var i = 0, len = gridArr.length; i < len; ++i) {
					var forGridId = +gridArr[i];
                    var value = roleHoroscopeGrids[forGridId];
                    if (value == petLevel) {
						petBoundRoleId = forRole.id;
						roleHoroscopeGrids[forGridId] = 0;
						player.calculateRoleHoroscopeAttrs(forRole);
						resp.data.bound = {rid: forRole.id, horoscope: forRole.horoscope};
						break;
                    }
                }

				if (petBoundRoleId) break;
            }
        }
        
        grids[gridId] = petLevel;
        player.calculateRoleHoroscopeAttrs(role);
        player.markDirty(util.format('role.%d.horoscope', rid));
        player.roleDirty = true;
        resp.data.horoscope = role.horoscope;
    } while (false);

    onHandled && onHandled();
}

exports.exchange_hsoul = function(player, req, resp, onHandled) {
    var user = player.user;

    do {
        if (isNaN(req.args.num)) {
            resp.code = 1; resp.desc = 'invalid args'; break;
        }

        var num = parseInt(req.args.num);
        if (num <= 0 || num >= 1000) {
            resp.code = 1; resp.desc = 'invalid num'; break;
        }

        var needSoul = num * gConfGlobal.HsoulExchangeRate;
        if (!player.costResource('soul', needSoul)) {
            resp.code = 1; resp.desc = 'lack of soul'; break;
        }

        player.alterResource('hsoul', num);
        resp.data.soul = -needSoul;
        resp.data.hsoul = num;
    } while (false);

    onHandled && onHandled();
}
